/*
 *  Copyright 2007-2008 by Matthias Buch-Kromann <mbk.isv@cbs.dk>.
 *  *
 *  This file is part of the Open-source Dependency Toolkit (OSDT),
 *  see http://code.google.com/p/open-source-dependency-toolkit.
 *  
 *  The OSDT is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as 
 *  published by the Free Software Foundation, either version 3 of 
 *  the License, or (at your option) any later version.
 * 
 *  The OSDT is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public 
 *  License along with the OSDT in the files COPYING and COPYING.LESSER.
 *  If not, see http://www.gnu.org/licenses.
 */

package org.osdtsystem.graphs.views;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.osdtsystem.exceptions.InconsistentViewException;
import org.osdtsystem.exceptions.LayerNotFoundException;
import org.osdtsystem.exceptions.NodeNotFoundException;
import org.osdtsystem.exceptions.NonUniqueNameException;
import org.osdtsystem.exceptions.OsdtSyntaxException;
import org.osdtsystem.graphs.Graph;
import org.osdtsystem.graphs.io.GraphIDs;
import org.osdtsystem.values.EdgeID;
import org.osdtsystem.values.Layer;
import org.osdtsystem.values.NodeID;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class WordAlignmentEngine implements WordAlignment {
    Graph graph;
    NodeCollection source;
    NodeCollection target;
    Layer alignmentLayer;
    
    public WordAlignmentEngine(final Graph graph, final NodeCollection source, 
            final NodeCollection target, final String name) throws NonUniqueNameException {
        this(graph, source, target, graph.addLayer(name + ViewConstants.WordAlignmentSuffix));
        graph.addView(this, name);
    }
    
    public WordAlignmentEngine(final Graph graph, final NodeCollection source,
            final NodeCollection target, final Layer alignmentLayer) {
        this.graph = graph;
        this.source = source;
        this.target = target;
        this.alignmentLayer = alignmentLayer;
    }
    
    @Override
    public List<NodeID> alignments() {
        return graph.nodes(alignmentLayer);
    }

    @Override
    public List<NodeID> alignments(NodeID node) {
        // Find set of all alignment edges originating from the source or target 
        // node, and record corresponding alignment nodes
        Set<NodeID> alignments = new HashSet<NodeID>();
        for (EdgeID edge : graph.outEdges(node, alignmentLayer))
            alignments.add(graph.inNode(edge));
        
        // Return list of alignment nodes
        return Arrays.asList(alignments.toArray(new NodeID[0]));
    }

    @Override
    public List<NodeID> nodes(NodeID alignment) {
        Set<NodeID> nodes = new HashSet<NodeID>();
        for (EdgeID edge : graph.inEdges(alignment)) {
            nodes.add(graph.outNode(edge));
        }
        return Arrays.asList(nodes.toArray(new NodeID[0]));
    }
    
    @Override
    public List<NodeID> sourceNodes(NodeID alignment) {
        Set<NodeID> sourceNodes = new HashSet<NodeID>();
        for (EdgeID edge : graph.inEdges(alignment)) {
            NodeID node = graph.outNode(edge);
            if (source.containsNode(node))
                sourceNodes.add(node);
        }
        return Arrays.asList(sourceNodes.toArray(new NodeID[0]));
    }

    @Override
    public List<NodeID> targetNodes(NodeID alignment) {
        Set<NodeID> targetNodes = new HashSet<NodeID>();
        for (EdgeID edge : graph.inEdges(alignment)) {
            NodeID node = graph.outNode(edge);
            if (target.containsNode(node))
                targetNodes.add(node);
        }
        return Arrays.asList(targetNodes.toArray(new NodeID[0]));
    }

    @Override
    public void remove(NodeID alignment) {
        for (EdgeID edge : graph.inEdges(alignment))
            graph.removeEdge(edge);
        graph.removeNode(alignment);
    }

    @Override
    public NodeID add(NodeID... nodes) throws NodeNotFoundException  {
        try {
            NodeID alignment = graph.addNode(alignmentLayer);
            for (NodeID node : nodes) 
                graph.addEdge(alignment, node, alignmentLayer);
            return alignment;
        } catch (LayerNotFoundException ex) {
            throw new InconsistentViewException("Alignment layer for WordAlignment " +
                    "viewer is not contained in graph", ex);
        }
    }

    @Override
    public void setGraph(Graph graph) {
        this.graph = graph;
    }

    @Override
    public View copy() {
        return new WordAlignmentEngine(graph, source, target, alignmentLayer);
    }

    @Override
    public void clear() {
        graph.clear(alignmentLayer);
    }
    
    // 1=view ID, 3=source node collection, 5=target node collection, 7=alignment layer
    private static final Pattern osdtWordAlignmentEnginePattern = Pattern.compile(
            "^\\s*VIEW([0-9]+)\\s+WordAlignmentEngine\\(VIEW)?([0-9]+),(NODE)?([0-9]+)," 
            + "(LAYER)?([0-9]+),(LAYER)?([0-9]+)\\)\\s*$");

    @Override
    public String toOSDT(Graph graph, GraphIDs ids) {
        return "WordAlignmentEngine(VIEW" + ids.id(source) + ","
                + "VIEW" + ids.id(target) + ","
                + "LAYER" + ids.id(alignmentLayer) + ")";
    }

    public static WordAlignmentEngine fromOSDT(Graph graph, GraphIDs ids, String osdt)
            throws OsdtSyntaxException {
        // Parse OSDT string
        Matcher m = osdtWordAlignmentEnginePattern.matcher(osdt);
        if (! m.find())
            return null;
        
        // Find view ID
        int viewID = Integer.parseInt(m.group(1));
        
        // Find source and target node collections, and alignment layer
        // 1=view ID, 3=source node collection, 5=target node collection, 7=alignment layer
        try {
            NodeCollection snodes = (NodeCollection) ids.getView(Integer.parseInt(m.group(3)));
            NodeCollection tnodes = (NodeCollection) ids.getView(Integer.parseInt(m.group(5)));
            Layer layer = ids.getLayer(Integer.parseInt(m.group(7)));
        
            // Create new word alignment engine
            return new WordAlignmentEngine(graph, snodes, tnodes, layer);
        } catch (Exception ex) {
            throw new OsdtSyntaxException("Cannot find view or layer in OSDT string: " + osdt);
        }
    }

    @Override
    public void recompute() {
        // TODO
    }    
}
